﻿using System;
using System.Collections.Generic;
using System.Threading;

namespace ArgusPCLib.Threading
{
	public class ThreadState
	{
		object[] _arguments;
		object _lock;
		ManualResetEvent waitHandle;

		public bool CancelRequested { get; private set; }
		//public Thread OwnerThread { get; private set; }
		public bool IsSuspended { get; private set; }
		public ArgumentCollection Arguments { get; private set; }

		private ThreadState()
		{
			this._lock = new object();
			this.CancelRequested = false;
			//this.OwnerThread = Thread.CurrentThread;
			this.waitHandle = new ManualResetEvent(true);
		}

		public ThreadState(params object[] arguments)
			: this()
		{
			this._arguments = arguments;
			this.Arguments = new ArgumentCollection(this);
		}

		public void RequestCancel()
		{
			this.CancelRequested = true;
			lock (this._lock)
			{
				if (this.IsSuspended == true)
				{
					this.waitHandle.Set();
					this.IsSuspended = false;
				}
			}
		}

		public void ResetCancelRequest()
		{
			this.CancelRequested = false;
		}

		public bool Suspend()
		{
			//if (this.OwnerThread != Thread.CurrentThread)
			//	throw new Exception("Only the owner thread may call suspend.");
			if (!Monitor.TryEnter(this._lock))
				return false;

			try
			{
				this.waitHandle.Reset();
				this.IsSuspended = true;
			}
			finally { Monitor.Exit(this._lock); }
			return true;
		}

		public bool Resume()
		{
			//if (this.OwnerThread != Thread.CurrentThread)
			//	throw new Exception("Only the owner thread may call resume.");

			if (!Monitor.TryEnter(this._lock))
				return false;

			try
			{
				this.waitHandle.Set();
				this.IsSuspended = false;
			}
			finally { Monitor.Exit(this._lock); }
			return true;
		}

		public void WaitForResumption()
		{
			//if (this.OwnerThread == Thread.CurrentThread)
			//	throw new Exception("The thread which created the object cannot wait. (Deadlock)");
			this.waitHandle.WaitOne();
		}

		public class ArgumentCollection : ArgusPCLib.IntIndexedPropertyGet<object>
		{
			ThreadState threadState;

			public ArgumentCollection(ThreadState threadState)
				:base()
			{
				this.threadState = threadState;
			}

			public override int Count { get { return this.threadState._arguments.Length; } }

			public override object this[int index] { get { return this.threadState._arguments[index]; } }
		}
	}
}
